/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 定义promise的三种状态，pending（等待），fulfilled（成功），rejected（失败）
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor (executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // 初始化promise状态
  status = PENDING
  // 成功返回值
  value = undefined
  // 失败返回值
  err = undefined
  // 存储异步成功回调函数
  successCallback = []
  // 存储异步失败回调函数
  failCallback = []

  static all (arr) {
    const results = []
    const len = arr.length
    let index = 0
    return new MyPromise((resolve, reject) => {
      const addData = (key, value) => {
        results[key] = value
        index += 1
        if (index === len) {
          resolve(results)
        }
      }
      for (let i = 0; i < len; i += 1) {
        if (arr[i] instanceof MyPromise) {
          arr[i].then(value => {
            addData(i, value)
          }, err => {
            reject(err)
          })
        } else {
          addData(i, arr[i])
        }
      }
    })
  }

  static race (arr) {
    const len = arr.length
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < len; i += 1) {
        if (arr[i] instanceof MyPromise) {
          arr[i].then(value => {
            resolve(value)
          }, err => {
            reject(err)
          })
        } else {
          resolve(arr[i])
        }
      }
    })
  }

  static resolve (value) {
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }

  static reject (err) {
    return new MyPromise((resolve, reject) => reject(err))
  }

  resolve = value => {
    // 当状态不为等待时阻止向下执行
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while (this.successCallback.length) this.successCallback.shift()()
  }

  reject = err => {
    // 当状态不为等待时阻止向下执行
    if (this.status !== PENDING) return
    this.status = REJECTED
    this.err = err
    while (this.failCallback.length) this.failCallback.shift()()
  }

  then (successCallback, failCallback) {
    // 将then的参数变为可选参数
    successCallback = successCallback || (value => value)
    failCallback = failCallback || (err => { throw err })
    // 为了实现链式调用，返回一个新的promise
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            const x = successCallback(this.value)
            resolvePromise(promise, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = failCallback(this.err)
            resolvePromise(promise, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else {
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              const x = successCallback(this.value)
              resolvePromise(promise, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0);
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              const x = failCallback(this.err)
              resolvePromise(promise, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0);
        })
      }
    })
    return promise
  }

  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
      callback()
    }, err => {
      return MyPromise.resolve(callback()).then(() => { throw err })
    })
  }

  catch (callback) {
    return this.then(undefined, callback)
  }
}

/* 
  判断x的值是普通值还是promise对象
  普通值直接调用resolve
  promise对象根据返回结果决定调用resolve还是reject
*/
function resolvePromise (promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
  }
  if (x instanceof MyPromise) {
    x.then(value => {
      resolve(value)
    }, err => {
      reject(err)
    })
  } else {
    resolve(x)
  }
}

module.exports = MyPromise
